/* 
 * File:   XMLParser.cpp
 * Author: Gab'z
 * 
 * Created on 14 juin 2012, 19:18
 */
#include "XMLParser.h"
#include "QFile"
#include "QString"


namespace calculator {

    Logger* XMLParser::log = LoggerManager::getInstance()->getLogger("XMLParser");

    XMLParser* XMLParser::instance = 0;

    XMLParser* XMLParser::getInstance() {
        if (instance == 0) instance = new XMLParser(Context::getInstance());
        return instance;
    }

    void XMLParser::deleteInstance() {
        if (instance != 0) delete instance;
    }

    XMLParser::XMLParser(Context * const context) : calculatorParser(new CalculatorParser(context)) {
    }

    XMLParser::~XMLParser() {
    }

    void XMLParser::loadContextFromFile(Context* context, const std::string& saveFilename) {
        log->debug("loadContextFromFile...");
        QFile file(saveFilename.c_str());
        if (!file.open(QIODevice::ReadOnly)) {
            log->debug("Can't open XML file or no " + saveFilename);
            return;
        }
        QDomDocument domDocument("CalculatriceNPI");
        domDocument.setContent(&file);



        QDomElement root = domDocument.documentElement();
        if (root.tagName() != "calculator") {
            return;
        }
        QDomElement parametersDomElement = root.firstChildElement("parameters");
        if (!parametersDomElement.isNull()) {
            log->debug("loading Parameters...");
            context->setParameters(NodeToParameters(parametersDomElement));
            log->debug("loading Parameters done");

        } else {
            log->error("Can't read parameters");
            return;
        }
        QDomElement stacklistDomElement = root.firstChildElement("stacklist");
        StackList* stacklist = context->getStackList();
        try {
            log->debug("loading stacklist...");
            if (!stacklistDomElement.isNull()) {
                //  stacklist
                //    
                for (std::list<Stack*>::const_iterator it = context->getStackList()->_stackList.begin();
                        it != context->getStackList()->_stackList.end();
                        it++) {
                    delete *it;
                }


                stacklist->_stackList.clear();
                QDomElement stackDomElement = stacklistDomElement.firstChildElement("stack");
                if (stackDomElement.isNull()) {
                    log->error("No stack in XML");
                }
                while (!stackDomElement.isNull()) {
                    log->debug("loading stack...");
                    Stack* stack = new Stack();
                    QDomElement constantDomElement = stackDomElement.firstChildElement("constant");
                    while (!constantDomElement.isNull()) {
                        log->debug("loading constant...");
                        const Constant* constant = NodeToConstant(constantDomElement);
                        stack->_constantStack.push_back(constant);
                        constantDomElement = constantDomElement.nextSiblingElement("constant");
                    }
                    log->debug("No more constant in stack");
                    stacklist->newStack(stack);
                    stackDomElement = stackDomElement.nextSiblingElement("stack");
                }
                log->debug("No more stack in XML");
                if (stacklist->size() == 0) {
                    log->error("No stack in XML");
                    stacklist->newStack();
                }
                stacklist->setCurrentStack(stacklistDomElement.attribute("currentStackIndex", 0).toUInt());
            } else {
                log->error("No stacklist in XML");
            }
            log->debug("loading stacklist done");
        } catch (std::exception& e) {
            log->error(e.what());
        }
        file.close();
        log->debug("loadContextFromFile done");
    }

    void XMLParser::saveContextToFile(const Context * const context, const std::string& saveFilename) {
        log->debug("saveContextToFile...");
        QDomDocument domDocument("CalculatriceNPI");
        QDomElement rootDomElement = domDocument.createElement("calculator");
        domDocument.appendChild(rootDomElement);
        rootDomElement.appendChild(parametersToNode(domDocument, context->getParameters()));
        QDomElement stacklistDomElement = stacklistToNode(domDocument, context->getStackList());

        for (std::list<Stack*>::const_iterator stackIt = context->getStackList()->_stackList.begin(); stackIt != context->getStackList()->_stackList.end(); stackIt++) {
            QDomElement stackDomElement = stackToNode(domDocument, *stackIt);
            for (std::list<const Constant*>::const_iterator constantIt = (*stackIt)->_constantStack.begin(); constantIt != (*stackIt)->_constantStack.end(); constantIt++) {
                QDomElement constantDomElement = constantToNode(domDocument, *constantIt);
                stackDomElement.appendChild(constantDomElement);
            }
            stacklistDomElement.appendChild(stackDomElement);
        }
        rootDomElement.appendChild(stacklistDomElement);

        QFile file(saveFilename.c_str());
        if (!file.open(QIODevice::WriteOnly)) {
            log->error("Can't open XML file " + saveFilename);
            return;
        }
        QTextStream textStream(&file);
        textStream << domDocument.toString();
        file.close();
        log->debug("saveContextToFile done");
    }

    QDomElement XMLParser::parametersToNode(QDomDocument& dom, const Parameters * const parameters) const {
        log->debug("parametersToNode");
        QDomElement parametersDomElement = dom.createElement("parameters");

        QDomElement constantTypeDomElement = dom.createElement("constantType");
        constantTypeDomElement.setAttribute("value", parameters->getConstantType());
        parametersDomElement.appendChild(constantTypeDomElement);

        QDomElement angleUnit = dom.createElement("angleUnit");
        angleUnit.setAttribute("value", parameters->getAngleUnit());
        parametersDomElement.appendChild(angleUnit);

        QDomElement complexMode = dom.createElement("complexMode");
        complexMode.setAttribute("value", parameters->getComplexMode());
        parametersDomElement.appendChild(complexMode);

        QDomElement displayKeyboard = dom.createElement("displayKeyboard");
        displayKeyboard.setAttribute("value", parameters->getDisplayKeyboard());
        parametersDomElement.appendChild(displayKeyboard);

        QDomElement visibleStackSize = dom.createElement("visibleStackSize");
        visibleStackSize.setAttribute("value", parameters->getVisibleStackSize());
        parametersDomElement.appendChild(visibleStackSize);

        QDomElement instantCompute = dom.createElement("instantCompute");
        instantCompute.setAttribute("value", parameters->getInstantCompute());
        parametersDomElement.appendChild(instantCompute);

        QDomElement integerDivision = dom.createElement("integerDivision");
        integerDivision.setAttribute("value", parameters->getIntegerDivision());
        parametersDomElement.appendChild(integerDivision);

        QDomElement saveOnExit = dom.createElement("saveOnExit");
        saveOnExit.setAttribute("value", parameters->getSaveOnExit());
        parametersDomElement.appendChild(saveOnExit);

        return parametersDomElement;
    }

    QDomElement XMLParser::stacklistToNode(QDomDocument& dom, const StackList * const stacklist) const {
        log->debug("stacklistToNode");
        QDomElement stacklistDomElement = dom.createElement("stacklist");
        stacklistDomElement.setAttribute("currentStackIndex", stacklist->getCurrentStackIndex());
        return stacklistDomElement;
    }

    QDomElement XMLParser::stackToNode(QDomDocument& dom, const Stack * const stack) const {
        log->debug("stackToNode");
        QDomElement stackDomElement = dom.createElement("stack");
        return stackDomElement;
    }

    QDomElement XMLParser::constantToNode(QDomDocument& dom, const Constant * const constant) const {
        log->debug("constantToNode");
        QDomElement constantDomElement = dom.createElement("constant");
        constantDomElement.setAttribute("name", constant->toString().c_str());
        return constantDomElement;
    }

    Parameters* XMLParser::NodeToParameters(QDomElement& parametersDomElement) const {
        Parameters::ConstantType constantType;
        switch (parametersDomElement.firstChildElement("constantType").attribute("value").toInt()) {
            case 0:
            {
                constantType = calculator::Parameters::INTEGER;
                break;
            }
            case 1:
            {
                constantType = calculator::Parameters::RATIONNAL;
                break;
            }
            case 2:
            {
                constantType = calculator::Parameters::REAL;
                break;
            }
            default:
            {
                // choisir integer par défaut
                constantType = calculator::Parameters::INTEGER;
                break;
            }
        }
        Parameters::AngleUnit angleUnit;
        switch (parametersDomElement.firstChildElement("angleUnit").attribute("value").toInt()) {
            case 0:
            {
                angleUnit = calculator::Parameters::DEGREE;
                break;
            }
            case 1:
            {
                angleUnit = calculator::Parameters::RADIAN;
                break;
            }
            default:
            {
                // choisir degree par défaut
                angleUnit = calculator::Parameters::DEGREE;
                break;
            }
        }
        return new Parameters(
                constantType,
                angleUnit,
                parametersDomElement.firstChildElement("complexMode").attribute("value").toInt(),
                parametersDomElement.firstChildElement("displayKeyboard").attribute("value").toInt(),
                parametersDomElement.firstChildElement("visibleStackSize").attribute("value").toInt(),
                parametersDomElement.firstChildElement("instantCompute").attribute("value").toInt(),
                parametersDomElement.firstChildElement("integerDivision").attribute("value").toInt(),
                parametersDomElement.firstChildElement("saveOnExit").attribute("value").toInt());
    }

    const Constant* XMLParser::NodeToConstant(QDomElement& constantDomElement) const {
        const Command* command = calculatorParser->parse(constantDomElement.attribute("name").toStdString());
        const Constant* constant = dynamic_cast<const Constant*> (command);
        if (constant == 0) throw ParseException("The value is not a Constant");
        return constant;
    }


}

